﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;

namespace Lossage.Net.Protocols.Modbus
{
    public class ModbusProtocol : ModbusModel
    {
        DateTime time;
        public DateTime Time { get => time; private set => time = value; }

        private byte getBitBufferCountFromRegCount(byte regCount)
        {
            return (byte)((regCount - 1) / 8 + 1);
        }

        /**/
        public void AnalyzeBytes(IEnumerable<byte> data, DirectionType direction)
        {
            this.Addr = data.ElementAt(0);
            this.Command = (CommandType)(data.ElementAt(1));
            this.Time = DateTime.Now;
            var buffer = data.Skip(2);
            switch (this.Command)
            {
                case CommandType.ReadCoilStatus:
                    this.readCoilAnalyze(buffer);
                    break;
                case CommandType.ReadInputStatus:
                    this.readInputAnalyze(buffer);
                    break;
                case CommandType.ReadHoldingRegisters:
                    this.readHoldingRegistersAnalyze(buffer);
                    break;
                case CommandType.ReadInputRegisters:
                    this.readInputRegistersAnalyze(buffer);
                    break;
                case CommandType.ForceSingleCoil:
                    this.forceSingleCoilAnalyze(buffer);
                    break;
                case CommandType.PresetSingleRegister:
                    this.presetSingleRegisterAnalyze(buffer);
                    break;
                case CommandType.ForceMultipleCoils:
                    this.forceMultipleCoilsAnalyze(buffer);
                    break;
                case CommandType.PresetMultipleRegisters:
                    this.presetMultipleRegistersAnalyze(buffer);
                    break;
                default:
                    throw new Exception("命令不支持");
            }
        }

        public IEnumerable<byte> AnalyzeStruct()
        {
            var buffer = new byte[2];
            buffer[0] = this.Addr;
            buffer[1] = (byte)(this.Command);
            this.Time = DateTime.Now;
            IEnumerable<byte> buffer2;
            switch (this.Command)
            {
                case CommandType.ReadCoilStatus:
                    buffer2 = this.readCoilCommand();
                    break;
                case CommandType.ReadInputStatus:
                    buffer2 = this.readInputCommand();
                    break;
                case CommandType.ReadHoldingRegisters:
                    buffer2 = this.readHoldingRegistersCommand();
                    break;
                case CommandType.ReadInputRegisters:
                    buffer2 = this.readInputRegistersCommand();
                    break;
                case CommandType.ForceSingleCoil:
                    buffer2 = this.forceSingleCoilCommand();
                    break;
                case CommandType.PresetSingleRegister:
                    buffer2 = this.presetSingleRegisterCommand();
                    break;
                case CommandType.ForceMultipleCoils:
                    buffer2 = this.forceMultipleCoilsCommand();
                    break;
                case CommandType.PresetMultipleRegisters:
                    buffer2 = this.presetMultipleRegistersCommand();
                    break;
                default:
                    throw new Exception("命令不支持");
            }
            if (buffer2 == null)
            {
                throw new Exception("解析错误");
            }
            else
            {
                return buffer.Concat(buffer2).ToArray();
            }
        }

        //Read Coil Status (FC=01)
        private IEnumerable<byte> readCoilCommand()
        {
            return this.readHoldingRegistersCommand();
        }

        //Read Input Status (FC=02)
        private IEnumerable<byte> readInputCommand()
        {
            return this.readHoldingRegistersCommand();
        }

        //Read Holding Registers (FC=03)
        private IEnumerable<byte> readHoldingRegistersCommand()
        {
            var buffer = new byte[4];
            buffer[0] = (byte)(this.RegNo >> 8);
            buffer[1] = (byte)(this.RegNo & 0xFF);
            buffer[2] = 0x00;
            buffer[3] = this.RegLength;
            return buffer;
        }

        //Read Input Registers (FC=04)
        private IEnumerable<byte> readInputRegistersCommand()
        {
            return this.readHoldingRegistersCommand();
        }

        //Force Single Coil (FC=05)
        private IEnumerable<byte> forceSingleCoilCommand()
        {
            if (this.Body == null)
            {
                throw new Exception("没有要写入的数据");
            }
            else if (this.Body.Length != 1)
            {
                throw new Exception("能且只能写入一个数据");
            }
            var buffer = new byte[4];
            buffer[0] = (byte)(this.RegNo >> 8);
            buffer[1] = (byte)(this.RegNo & 0xFF);
            if (this.Body[0] == 0)
            {
                buffer[2] = 0x00;
            }
            else
            {
                buffer[2] = 0xFF;
            }
            buffer[3] = 0x00;
            return buffer;
        }

        //Preset Single Register (FC=06)
        private IEnumerable<byte> presetSingleRegisterCommand()
        {
            if (this.Body == null)
            {
                throw new Exception("没有要写入的数据");
            }
            else if (this.Body.Length != 1)
            {
                throw new Exception("能且只能写入一个数据");
            }
            var buffer = new byte[4];
            buffer[0] = (byte)(this.RegNo >> 8);
            buffer[1] = (byte)(this.RegNo & 0xFF);
            buffer[2] = (byte)(this.Body[0] >> 8);
            buffer[3] = (byte)(this.Body[0] & 0xFF);
            return buffer;
        }

        //Force Multiple Coils (FC=15)
        private IEnumerable<byte> forceMultipleCoilsCommand()
        {
            if (this.Body == null)
            {
                throw new Exception("没有要写入的数据");
            }
            else if (this.Body.Length < this.RegLength)
            {
                throw new Exception("数据长度不够");
            }
            this.BufferLen = getBitBufferCountFromRegCount(this.RegLength);
            var buffer = new byte[5 + this.BufferLen];
            buffer[0] = (byte)(this.RegNo >> 8);
            buffer[1] = (byte)(this.RegNo & 0xFF);
            buffer[2] = 0x00;
            buffer[3] = this.RegLength;
            buffer[4] = this.BufferLen;
            for (var i = 0; i < this.RegLength; i++)
            {
                if (this.Body[i] == 0)
                {
                    buffer[5 + i / 8] = (byte)(buffer[5 + i / 8] | (0x01 << (i % 8)));
                }
            }
            return buffer;
        }

        //Preset Multiple Registers (FC=16)
        private IEnumerable<byte> presetMultipleRegistersCommand()
        {
            if (this.Body == null)
            {
                throw new Exception("没有要写入的数据");
            }
            else if (this.Body.Length < this.RegLength)
            {
                throw new Exception("数据长度不够");
            }
            if (this.RegLength > 127)
            {
                throw new Exception("数据长度不能超过127");
            }
            this.BufferLen = (byte)(this.RegLength * 2);
            var buffer = new byte[5 + this.RegLength * 2];
            buffer[0] = (byte)(this.RegNo >> 8);
            buffer[1] = (byte)(this.RegNo & 0xFF);
            buffer[2] = 0x00;
            buffer[3] = this.RegLength;
            buffer[4] = this.BufferLen;
            for (var i = 0; i < this.RegLength; i++)
            {
                buffer[5 + 2*i] = (byte)(this.Body[i] >> 8);
                buffer[6 + 2*i ] = (byte)(this.Body[i] & 0xFF);
            }
            return buffer;
        }

        //Read Coil Status (FC=01)
        private void readCoilAnalyze(IEnumerable<byte> buffer)
        {
            if (buffer == null || buffer.Count() < 1)
            {
                throw new Exception("数据长度不够");
            }
            this.BufferLen = buffer.ElementAt(0);
            if (this.BufferLen > (buffer.Count() - 1))
            {
                throw new Exception("数据长度不够");
            }
            if (this.BufferLen != getBitBufferCountFromRegCount(this.RegLength))
            {
                throw new Exception("数据长度与读取的要求不匹配");
            }
            this.Body = new UInt16[this.RegLength];
            for (var i = 0; i < this.BufferLen; i++)
            {
                for (var j = 0; j < 8; j++)
                {
                    if (i * 8 + j < this.RegLength)
                    {
                        this.Body[i * 8 + j] = (UInt16)(((UInt16)buffer.ElementAt(i + 1) >> j) & 0x0001);
                    }
                }
            }
            return;
        }

        //Read Input Status (FC=02)
        private void readInputAnalyze(IEnumerable<byte> buffer)
        {
            this.readCoilAnalyze(buffer);
        }

        //Read Holding Registers (FC=03)
        private void readHoldingRegistersAnalyze(IEnumerable<byte> buffer)
        {
            if (buffer == null || buffer.Count() < 1)
            {
                throw new Exception("数据长度不够");
            }
            this.BufferLen = buffer.ElementAt(0);
            if (this.BufferLen > (buffer.Count() - 1))
            {
                throw new Exception("数据长度不够");
            }
            if (this.BufferLen != this.RegLength * 2)
            {
                throw new Exception("数据长度与读取的要求不匹配");
            }
            this.Body = new UInt16[this.BufferLen / 2];
            for (var i = 1; i < this.BufferLen; i = i + 2)
            {
                this.Body[i / 2] = (UInt16)((UInt16)buffer.ElementAt(i) << 8 | (UInt16)buffer.ElementAt(i + 1));
            }
            return;
        }

        //Read Input Registers (FC=04)
        private void readInputRegistersAnalyze(IEnumerable<byte> buffer)
        {
            this.readHoldingRegistersAnalyze(buffer);
            return;
        }

        //Force Single Coil (FC=05)
        private void forceSingleCoilAnalyze(IEnumerable<byte> buffer)
        {
            if (buffer == null || buffer.Count() != 4)
            {
                throw new Exception("数据长度不够");
            }
            this.RegNo = (UInt16)((UInt16)buffer.ElementAt(0) << 8 | (UInt16)buffer.ElementAt(1));
            this.Body = new UInt16[1];
            this.Body[0] = (UInt16)((UInt16)(buffer.ElementAt(2)) << 8 | (UInt16)(buffer.ElementAt(3)));
            return;
        }

        //Preset Single Register (FC=06)
        private void presetSingleRegisterAnalyze(IEnumerable<byte> buffer)
        {
            if (buffer == null || buffer.Count() != 4)
            {
                throw new Exception("数据长度不够");
            }
            this.RegNo = (UInt16)((UInt16)buffer.ElementAt(0) << 8 | (UInt16)buffer.ElementAt(1));
            this.Body = new UInt16[1];
            if (buffer.ElementAt(2) == 0x00 && buffer.ElementAt(3) == 0x00)
            {
                this.Body[0] = 0;
            }
            else if (buffer.ElementAt(2) == 0xff && buffer.ElementAt(3) == 0x00)
            {
                this.Body[0] = 1;
            }
            else
            {
                throw new Exception("返回的寄存器值错误");
            }
            return;
        }

        //Force Multiple Coils (FC=15)
        private void forceMultipleCoilsAnalyze(IEnumerable<byte> buffer)
        {
            if (buffer == null || buffer.Count() != 4)
            {
                throw new Exception("数据长度不够");
            }
            //_this.setPropertyHas(true, true, false, false)
            this.RegNo = (UInt16)((UInt16)buffer.ElementAt(0) << 8 | (UInt16)buffer.ElementAt(1));
            this.RegLength = buffer.ElementAt(3);
            return;
        }

        //Preset Multiple Registers (FC=16)
        private void presetMultipleRegistersAnalyze(IEnumerable<byte> buffer)
        {
            this.forceMultipleCoilsAnalyze(buffer);
        }

    }
}
